package org.ourgrid.broker.scheduler.workqueue;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javax.jws.WebMethod;
import javax.jws.WebService;

import org.ourgrid.broker.commands.SchedulerData;
import org.ourgrid.broker.controller.messages.BrokerControlMessages;
import org.ourgrid.broker.controller.messages.LocalWorkerProviderClientMessages;
import org.ourgrid.broker.controller.messages.SchedulerMessages;
import org.ourgrid.broker.controller.messages.WorkerClientMessages;
import org.ourgrid.broker.controller.operations.GridProcessOperations;
import org.ourgrid.broker.controller.operations.ReplicaParser;
import org.ourgrid.broker.dao.Request;
import org.ourgrid.broker.dao.WorkerEntry;
import org.ourgrid.broker.scheduler.SchedulerIF;
import org.ourgrid.broker.scheduler.extensions.GenericTransferProgress;
import org.ourgrid.broker.scheduler.extensions.IncomingHandle;
import org.ourgrid.broker.scheduler.extensions.OutgoingHandle;
import org.ourgrid.broker.scheduler.workqueue.xmlcreator.AddJobXMLCreator;
import org.ourgrid.broker.scheduler.workqueue.xmlcreator.DisposeWorkerXMLCreator;
import org.ourgrid.broker.scheduler.workqueue.xmlcreator.FinishRequestXMLCreator;
import org.ourgrid.broker.scheduler.workqueue.xmlcreator.JobEndedXMLCreator;
import org.ourgrid.broker.scheduler.workqueue.xmlcreator.LoggerXMLCreator;
import org.ourgrid.broker.scheduler.workqueue.xmlcreator.OperationSucceedXMLCreator;
import org.ourgrid.broker.scheduler.workqueue.xmlcreator.PauseRequestXMLCreator;
import org.ourgrid.broker.scheduler.workqueue.xmlcreator.RequestWorkersXMLCreator;
import org.ourgrid.broker.scheduler.workqueue.xmlcreator.StartWorkXMLCreator;
import org.ourgrid.broker.scheduler.workqueue.xmlcreator.UnwantWorkerXMLCreator;
import org.ourgrid.broker.scheduler.workqueue.xmlcreator.UpdateSchedulerXMLCreator;
import org.ourgrid.broker.scheduler.workqueue.xmlcreator.XMLCreatorFactory;
import org.ourgrid.broker.scheduler.workqueue.xmlcreator.XMLCreatorIF;
import org.ourgrid.broker.status.GridProcessStatusInfo;
import org.ourgrid.broker.status.GridProcessStatusInfoResult;
import org.ourgrid.broker.status.JobStatusInfo;
import org.ourgrid.broker.status.JobWorkerStatus;
import org.ourgrid.broker.status.TaskStatusInfo;
import org.ourgrid.broker.status.WorkerStatusInfo;
import org.ourgrid.broker.util.UtilConverter;
import org.ourgrid.common.executor.ExecutorResult;
import org.ourgrid.common.filemanager.FileInfo;
import org.ourgrid.common.interfaces.to.GridProcessErrorTypes;
import org.ourgrid.common.interfaces.to.GridProcessHandle;
import org.ourgrid.common.interfaces.to.GridProcessState;
import org.ourgrid.common.interfaces.to.MessageHandle;
import org.ourgrid.common.interfaces.to.RequestSpec;
import org.ourgrid.common.job.GridProcess;
import org.ourgrid.common.job.Job;
import org.ourgrid.common.job.Task;
import org.ourgrid.common.spec.job.JobSpec;
import org.ourgrid.common.spec.worker.WorkerSpec;
import org.ourgrid.peer.controller.matcher.MatcherImpl;
import org.ourgrid.worker.controller.GridProcessError;

import br.edu.ufcg.lsd.commune.identification.DeploymentID;

@WebService()
public class WorkQueueReplication implements SchedulerIF {
			
	private WorkQueueExecutionController executionController;

	public WorkQueueReplication() {
	}
	
	
	public WorkQueueReplication(int maxReplicas, int maxFails, int maxBlFails) {
		this.executionController = new WorkQueueExecutionController(maxReplicas, maxFails, maxBlFails);
	}
	
	@WebMethod
	public void setData(int maxReplicas, int maxFails, int maxBlFails) {
		this.executionController = new WorkQueueExecutionController(maxReplicas, maxFails, maxBlFails);
	}
	
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#start()
	 */
	@WebMethod
	public List<SchedulerData> start() {
		JobInfo.reset();
		WorkerInfo.reset();
		PeerInfo.reset();
		
		return null;
	}

	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#stop()
	 */
	@WebMethod
	public List<SchedulerData> stop() {
		return null;
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#schedule()
	 */
	@WebMethod
	public List<SchedulerData> schedule() {
		
		List<SchedulerData> data = new ArrayList<SchedulerData>();
		
		for (Job job : getJobInfo().getJobsList()) {
			
			this.executionController.schedule(job);
			data.addAll(execute(job));
			data.addAll(cleanJob(job));
		}
		
		return data;
	}
	
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#addJob(org.ourgrid.common.interfaces.control.BrokerControlClient, org.ourgrid.common.spec.job.JobSpec)
	 */
	@WebMethod
	public List<SchedulerData> addJob(JobSpec jobSpec, int jobID) {
		
		Job job = getJobInfo().addJob(jobSpec, jobID);
		List<SchedulerData> data = new ArrayList<SchedulerData>();
		
		SchedulerData rwData = null;
		for (String peerID : getPeerInfo().getLoggedPeersIDs()) {
			RequestSpec requestSpec = this.executionController.createRequestSpec(job.getJobId(), job.getSpec());
			job.addRequest(requestSpec, peerID);
			
			rwData = new SchedulerData(getXmlCreator(RequestWorkersXMLCreator.class).
					getXML(peerID, requestSpec.getRequestId(), jobID, requestSpec.getRequiredWorkers(),
							requestSpec.getMaxFails(), requestSpec.getMaxReplicas()));
			
			data.add(rwData);
		}
		
		SchedulerData loggerData = new SchedulerData(getXmlCreator(LoggerXMLCreator.class).
				getXML(BrokerControlMessages.getJobAddedMessage(jobSpec, job.getJobId()), 
						LoggerXMLCreator.DEBUG));
		
		SchedulerData addJobData = new SchedulerData(getXmlCreator(AddJobXMLCreator.class).
				getXML(jobID, null));
		
		data.add(loggerData);
		data.add(addJobData);
		
		data.addAll(getCollector().getData());
		return data;
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#cancelJob(org.ourgrid.common.interfaces.control.BrokerControlClient, org.ourgrid.common.job.Job)
	 */
	@WebMethod
	public List<SchedulerData> cancelJob(int jobID) {
		
		Job job = getJobInfo().getJob(jobID);
		
		if  (job == null || job.getState().equals(GridProcessState.CANCELLED)){
			
			SchedulerData cancelJob = new SchedulerData(getXmlCreator(OperationSucceedXMLCreator.class).
					getXML(BrokerControlMessages.getNoSuchJobToCancelMessage(jobID), null));
			
			SchedulerData loggerData = new SchedulerData(getXmlCreator(LoggerXMLCreator.class).
					getXML(BrokerControlMessages.getNoSuchJobToCancelMessage(jobID), 
							LoggerXMLCreator.WARN));
			
			return createDataList(cancelJob, loggerData);
		}
		
		List<SchedulerData> data = new ArrayList<SchedulerData>();
		
		SchedulerData frData = null;
		RequestSpec spec = null;
		for (Request request : job.getRequests()) {
			spec = request.getSpec();
			
			frData = new SchedulerData(getXmlCreator(FinishRequestXMLCreator.class).
					getXML(spec.getRequestId(), request.getPeerID(), spec.getJobId(), spec.getRequiredWorkers(),
							spec.getMaxFails(), spec.getMaxReplicas()));
			
			data.add(frData);
		}
		
		cancelJob(job);
		
		Set<String> interestedSet = getJobInfo().getInterested(job.getJobId());
		
		if (interestedSet != null) {
			
			SchedulerData jeData = null;
			for (String interestedID : interestedSet) {
				
				jeData = new SchedulerData(getXmlCreator(JobEndedXMLCreator.class).
						getXML(jobID, JobStatusInfo.CANCELLED, interestedID));
				
				data.add(jeData);
			}
		}
		
		SchedulerData cancelJob = new SchedulerData(getXmlCreator(OperationSucceedXMLCreator.class).
				getXML(null, null));
		
		SchedulerData loggerData = new SchedulerData(getXmlCreator(LoggerXMLCreator.class).
				getXML(BrokerControlMessages.getJobCancelledMessage(jobID), 
						LoggerXMLCreator.DEBUG));
		
		data.add(cancelJob);
		data.add(loggerData);
		
		data.addAll(getCollector().getData());
		
		return data;
	}
	


	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#cleanAllFinishedJobs(org.ourgrid.common.interfaces.control.BrokerControlClient)
	 */
	@WebMethod
	public List<SchedulerData> cleanAllFinishedJobs() {
		getJobInfo().cleanAllFinishedJobs();
		
		List<SchedulerData> data = new ArrayList<SchedulerData>();
		
		SchedulerData opData = new SchedulerData(getXmlCreator(OperationSucceedXMLCreator.class).
				getXML(null, null));
		
		data.add(opData);
		data.addAll(getCollector().getData());
		
		return data;
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#cleanFinishedJob(org.ourgrid.common.interfaces.control.BrokerControlClient, int)
	 */
	@WebMethod
	public List<SchedulerData> cleanFinishedJob(int jobID) {
		
		Job job = getJobInfo().getJob(jobID);
		
		List<SchedulerData> data = new ArrayList<SchedulerData>();
		
		if (job.isRunning()) {
			
			SchedulerData loggerData = new SchedulerData(getXmlCreator(LoggerXMLCreator.class).
					getXML(BrokerControlMessages.getJobStillRunningMessage(jobID), 
							LoggerXMLCreator.ERROR));
			
			return createDataList(loggerData);
		}
		
		getJobInfo().cleanFinishedJob(jobID);
		
		SchedulerData opData = new SchedulerData(getXmlCreator(OperationSucceedXMLCreator.class).
				getXML(null, null));
		
		data.add(opData);
		data.addAll(getCollector().getData());
		
		return data;
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#loginSucceed(java.lang.String)
	 */
	@WebMethod
	public List<SchedulerData> loginSucceed(String peerID) {
		
		List<SchedulerData> data = new ArrayList<SchedulerData>();
		RequestSpec spec = null;
		SchedulerData rwData = null;
		
		for (Job job : getJobInfo().getJobsList()) {
			if (!this.executionController.isJobSatisfied(job)) {
				spec = this.executionController.createRequestSpec(job.getJobId(), job.getSpec());
				
				rwData = new SchedulerData(getXmlCreator(RequestWorkersXMLCreator.class).
						getXML(peerID, spec.getRequestId(), job.getJobId(), spec.getRequiredWorkers(),
								spec.getMaxFails(), spec.getMaxReplicas()));
				
				data.add(rwData);
				job.addRequest(spec, peerID);
			}
		}
		
		getPeerInfo().addPeerLogged(peerID);
		
		data.addAll(getCollector().getData());
		
		return data;
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#setPeers(java.lang.String[])
	 */
	@WebMethod
	public List<SchedulerData> setPeers(String[] peersID) {
		
		getPeerInfo().removePeersLogged();
		return null;
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#hereIsWorker(java.lang.String, java.lang.String, java.lang.String, java.lang.String, long)
	 */
	@WebMethod
	public List<SchedulerData> hereIsWorker(String workerID, String workerPK, String senderPublicKey, 
			String peerID, RequestSpec requestSpec, WorkerSpec workerSpec) {
		
		if (!getJobInfo().hasRunningJobs()) {
			
			SchedulerData loggerData = new SchedulerData(getXmlCreator(LoggerXMLCreator.class).
					getXML(LocalWorkerProviderClientMessages.getBrokerWithoutRunningJobsReceivingWorkerMessage(
							workerPK, senderPublicKey), 
							LoggerXMLCreator.WARN));
			
			SchedulerData disposeData = new SchedulerData(getXmlCreator(DisposeWorkerXMLCreator.class).
					getXML(peerID, workerID));
			
			return createDataList(loggerData, disposeData);
		}
		
		long requestID = requestSpec.getRequestId();
		Job job = getJobInfo().getJobForThisRequest(requestID);
		Request request = job.getRequest(requestID);
		
		List<SchedulerData> data = new ArrayList<SchedulerData>();
		
		if (job != null) {
			
			if (!request.isPaused()) {
				
				if (!this.executionController.isWorkerUnwanted(job, workerID)) {
					data.addAll(addJob(workerSpec, requestID, job, workerID));
				} else {
					
					SchedulerData uwData = new SchedulerData(getXmlCreator(UnwantWorkerXMLCreator.class).
							getXML(peerID, workerID, requestID, requestSpec.getJobId(), 
									requestSpec.getRequiredWorkers(), requestSpec.getMaxFails(), 
									requestSpec.getMaxFails()));
					
					data.add(uwData);
				}
			} else {
				
				SchedulerData pData = new SchedulerData(getXmlCreator(PauseRequestXMLCreator.class).
						getXML(peerID, requestSpec.getRequestId()));
				
				SchedulerData dwData = new SchedulerData(getXmlCreator(DisposeWorkerXMLCreator.class).
						getXML(peerID, workerID));
				
				data.add(pData);
				data.add(dwData);
			}
			
		} else {
			SchedulerData dwData = new SchedulerData(getXmlCreator(DisposeWorkerXMLCreator.class).
					getXML(peerID, workerID));
			
			data.add(dwData);
		}
		
		data.addAll(getCollector().getData());
		return data;
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#localWorkerProviderFailure(java.lang.String)
	 */
	@WebMethod
	public List<SchedulerData> localWorkerProviderFailure(String peerID) {
		
		for (Job job :getJobInfo().getJobsList()) {
			job.removeRequests(peerID);
			
		}
		
		getPeerInfo().removePeerLogged(peerID);
		return getCollector().getData();
	}
	
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#sendMessage(org.ourgrid.common.interfaces.to.MessageHandle)
	 */
	@WebMethod
	public List<SchedulerData> sendMessage(MessageHandle messageHandle) {
		return getCollector().getData();
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#incomingTransferCompleted(org.ourgrid.broker.scheduler.transferhandle.IncomingHandle, long)
	 */
	@WebMethod
	public List<SchedulerData> incomingTransferCompleted(IncomingHandle handle, long amountWritten) {
		
		WorkerEntry workerEntry = getWorkerEntry(handle.getSenderContainerID().toString());	
		
		if (workerEntry == null) {
			return createDataList(logInvalidWorkerEntry(handle.getSenderContainerID().toString()));
		}
		
		if(!continueExecution(workerEntry)) {
			return null;
		}
		
		GridProcess execution = workerEntry.getGridProcess();
		execution.getRunningState().incomingTransferCompleted(handle, amountWritten, execution);
		return getCollector().getData();
	}

	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#incomingTransferFailed(org.ourgrid.broker.scheduler.transferhandle.IncomingHandle, java.lang.String, long)
	 */
	@WebMethod
	public List<SchedulerData> incomingTransferFailed(IncomingHandle handle, String failCause, long amountWritten) {
		
		WorkerEntry workerEntry = getWorkerEntry(handle.getSenderContainerID().toString());	
		
		if (workerEntry == null) {
			return createDataList(logInvalidWorkerEntry(handle.getSenderContainerID().toString()));
		}
		
		if(!continueExecution(workerEntry)) {
			return null;
		}
		
		GridProcess execution = workerEntry.getGridProcess();
		execution.getRunningState().incomingTransferFailed(handle, new Exception(failCause), amountWritten, 
				execution);
		return getCollector().getData();
	}

	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#outgoingTransferCancelled(br.edu.ufcg.lsd.commune.processor.filetransfer.OutgoingTransferHandle, long)
	 */
	@WebMethod
	public List<SchedulerData> outgoingTransferCancelled(OutgoingHandle handle, long amountWritten) {
		
		WorkerEntry workerEntry = getWorkerEntry(handle.getDestinationContainerID());	
		
		if (workerEntry == null) {
			return createDataList(logInvalidWorkerEntry(handle.getDestinationContainerID()));
		}
		
		if(!continueExecution(workerEntry)) {
			return null;
		}
		
		GridProcess execution = workerEntry.getGridProcess();
		execution.getRunningState().outgoingTransferCancelled(handle, amountWritten, execution);
		return getCollector().getData();
	}

	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#outgoingTransferCompleted(br.edu.ufcg.lsd.commune.processor.filetransfer.OutgoingTransferHandle, long)
	 */
	@WebMethod
	public List<SchedulerData> outgoingTransferCompleted(OutgoingHandle handle, long amountWritten) {
		
		WorkerEntry workerEntry = getWorkerEntry(handle.getDestinationContainerID());	
		
		if (workerEntry == null) {
			return createDataList(logInvalidWorkerEntry(handle.getDestinationContainerID()));
		}
		
		if(!continueExecution(workerEntry)) {
			return null;
		}
		
		GridProcess execution = workerEntry.getGridProcess();
		execution.getRunningState().outgoingTransferCompleted(handle, amountWritten, execution);
		return getCollector().getData();
	}

	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#outgoingTransferFailed(br.edu.ufcg.lsd.commune.processor.filetransfer.OutgoingTransferHandle, java.lang.Exception, long)
	 */
	@WebMethod
	public List<SchedulerData> outgoingTransferFailed(OutgoingHandle handle, String failCause, long amountWritten) {
		
		WorkerEntry workerEntry = getWorkerEntry(handle.getDestinationContainerID());	
		
		if (workerEntry == null) {
			return createDataList(logInvalidWorkerEntry(handle.getDestinationContainerID()));
		}
		
		if(!continueExecution(workerEntry)) {
			return null;
		}
		
		GridProcess execution = workerEntry.getGridProcess();
		execution.getRunningState().outgoingTransferFailed(handle, failCause, amountWritten, execution);
		return getCollector().getData();
	}

	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#transferRejected(br.edu.ufcg.lsd.commune.processor.filetransfer.OutgoingTransferHandle)
	 */
	@WebMethod
	public List<SchedulerData> transferRejected(OutgoingHandle handle) {
	
		WorkerEntry workerEntry = getWorkerEntry(handle.getDestinationContainerID());	
		
		if (workerEntry == null) {
			return createDataList(logInvalidWorkerEntry(handle.getDestinationContainerID()));
		}
		
		if(!continueExecution(workerEntry)) {
			return null;
		}
		
		GridProcess execution = workerEntry.getGridProcess();
		execution.getRunningState().fileRejected(handle, execution);
		return getCollector().getData();
	}

	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#transferRequestReceived(br.edu.ufcg.lsd.commune.processor.filetransfer.IncomingTransferHandle)
	 */
	@WebMethod
	public List<SchedulerData> transferRequestReceived(IncomingHandle handle) {
		
		WorkerEntry workerEntry = getWorkerEntry(handle.getSenderContainerID().toString());	
		
		if (workerEntry == null) {
			return createDataList(logInvalidWorkerEntry(handle.getSenderContainerID().toString()));
		}
		
		if(!continueExecution(workerEntry)) {
			return null;
		}
		
		GridProcess execution = workerEntry.getGridProcess();
		execution.getRunningState().fileTransferRequestReceived(handle, execution);
		return getCollector().getData();
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#errorOcurred(java.lang.String, java.lang.String, org.ourgrid.common.interfaces.to.GridProcessErrorTypes)
	 */
	@WebMethod
	public List<SchedulerData> errorOcurred(String workerContainerID, String errorCause, 
			String gridProcessErrorType) {
		
		WorkerEntry workerEntry = getWorkerEntry(workerContainerID);	
		
		if (workerEntry == null) {
			return createDataList(logInvalidWorkerEntry(workerContainerID));
		}
		
		if(!continueExecution(workerEntry)) {
			return null;
		}
		
		GridProcess gridProcess = workerEntry.getGridProcess();
		GridProcessError error = new GridProcessError(new Throwable(errorCause), 
				GridProcessErrorTypes.getType(gridProcessErrorType)); 
		gridProcess.getRunningState().errorOcurred(error, gridProcess);
		
		return getCollector().getData();
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#hereIsFileInfo(java.lang.String, br.edu.ufcg.lsd.commune.processor.filetransfer.TransferHandle, org.ourgrid.common.filemanager.FileInfo)
	 */
	@WebMethod
	public List<SchedulerData> hereIsFileInfo(String workerContainerID, long handlerID,	FileInfo fileInfo) {
		
		WorkerEntry workerEntry = getWorkerEntry(workerContainerID);	
		
		if (workerEntry == null) {
			return createDataList(logInvalidWorkerEntry(workerContainerID));
		}
		
		if(!continueExecution(workerEntry)) {
			return null;
		}
		
		GridProcess gridProcess = workerEntry.getGridProcess();
		gridProcess.getRunningState().hereIsFileInfo(handlerID, fileInfo, gridProcess);
		
		return getCollector().getData();
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#hereIsGridProcessResult(java.lang.String, org.ourgrid.common.executor.ExecutorResult)
	 */
	@WebMethod
	public List<SchedulerData> hereIsGridProcessResult(String workerContainerID, ExecutorResult result) {
		
		WorkerEntry workerEntry = getWorkerEntry(workerContainerID);	
		
		if (workerEntry == null) {
			return createDataList(logInvalidWorkerEntry(workerContainerID));
		}
		
		if(!continueExecution(workerEntry)) {
			return null;
		}
		
		GridProcess gridProcess = workerEntry.getGridProcess();
		gridProcess.getRunningState().hereIsExecutionResult(result, gridProcess);
		
		return getCollector().getData();
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#workerIsReady(java.lang.String)
	 */
	@WebMethod
	public List<SchedulerData> workerIsReady(String workerContainerID) {
		
		WorkerEntry workerEntry = getWorkerEntry(workerContainerID);	
		
		if (workerEntry == null) {
			return createDataList(logInvalidWorkerEntry(workerContainerID));
		}
		
		if(!continueExecution(workerEntry)) {
			return null;
		}
		
		GridProcess gridProcess = workerEntry.getGridProcess();
		gridProcess.getRunningState().workerIsReady(gridProcess);
		
		return getCollector().getData();
	}
	

	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#updateTransferProgress(br.edu.ufcg.lsd.commune.processor.filetransfer.TransferProgress)
	 */
	@WebMethod
	public List<SchedulerData> updateTransferProgress(GenericTransferProgress transferProgress) {
		WorkerEntry workerEntry = getWorkerEntry(transferProgress.getHandle().getOppositeID().toString());	
		
		if (workerEntry == null) {
			return createDataList(logInvalidWorkerEntry(transferProgress.getHandle().getOppositeID().toString()));
		}
		
		if(!continueExecution(workerEntry)) {
			return null;
		}
		
		GridProcess execution = workerEntry.getGridProcess();
		execution.getRunningState().updateTransferProgress(transferProgress, execution);
		return getCollector().getData();
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#workerFailure(java.lang.String)
	 */
	@WebMethod
	public List<SchedulerData> workerFailure(String workerContinerID, String workerPublicKey) {
		
		WorkerEntry workerEntry =  getWorkerInfo().removeWorker(workerContinerID);
		
		if (workerEntry != null) {
			
			GridProcess gridProcess = workerEntry.getGridProcess();
			if (gridProcess != null) {
				gridProcess.getResult().setExecutionError(
						new GridProcessError(GridProcessErrorTypes.MACHINE_FAILURE));
				
				gridProcess.setGridProcessResult(GridProcessState.FAILED);
				
				GridProcessOperations operations = gridProcess.getOperations();
				if (operations != null) {
					operations.cancelOperations();
				}
				
				this.executionController.executionFailed(gridProcess);
			}
			workerEntry.dispose();
			
		} else {
			
			SchedulerData loggerData = new SchedulerData(getXmlCreator(LoggerXMLCreator.class).
					getXML(WorkerClientMessages.getPeerSendAWorkerFailureMessage(workerPublicKey), 
							LoggerXMLCreator.WARN));
			
			getCollector().addData(loggerData);
		}
		
		return getCollector().getData();
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#notifyWhenJobIsFinished(int, java.lang.String)
	 */
	@WebMethod
	public List<SchedulerData> notifyWhenJobIsFinished(int jobID, String deploymentID) {
		
		Job job = getJobInfo().getJob(jobID);
		
		List<SchedulerData> data = new ArrayList<SchedulerData>();
		
		if (job != null && !job.getState().isRunnable()) {
			int jobState = UtilConverter.getJobState(job.getState());
			SchedulerData jeData = new SchedulerData(getXmlCreator(JobEndedXMLCreator.class).
					getXML(jobID, jobState, deploymentID));
			data.add(jeData);
		}
		
		getJobInfo().notifyWhenJobIsFinished(jobID, deploymentID);
		
		data.addAll(getCollector().getData());
		
		return data;
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#finishRequests()
	 */
	@WebMethod
	public List<SchedulerData> finishRequests() {
		
		List<SchedulerData> data = new ArrayList<SchedulerData>();
		SchedulerData frData = null;
		RequestSpec spec = null;
		for (Job job : getJobInfo().getJobsList()) {
			for (Request request : job.getRequests()) {
				spec = request.getSpec();
				
				frData = new SchedulerData(getXmlCreator(FinishRequestXMLCreator.class).
						getXML(spec.getRequestId(), request.getPeerID(), spec.getJobId(), spec.getRequiredWorkers(),
								spec.getMaxFails(), spec.getMaxReplicas()));
				
				data.add(frData);
			}
			job.finishRequests();
		}
		
		data.addAll(getCollector().getData());
		
		return data;
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#finishRequests(java.lang.String)
	 */
	@WebMethod
	public List<SchedulerData> finishPeerRequests(String peerContainerID) {
		
		List<SchedulerData> data = new ArrayList<SchedulerData>();
		SchedulerData frData = null;
		DeploymentID peerID = null;
		RequestSpec spec = null;
		for (Job job : getJobInfo().getJobsList()) {
			for (Request request : job.getRequests()) {
				spec = request.getSpec();
				
				peerID = new DeploymentID(request.getPeerID());
				if (peerID.getContainerID().toString().equals(peerContainerID)) {
					
					frData = new SchedulerData(getXmlCreator(FinishRequestXMLCreator.class).
							getXML(spec.getRequestId(), request.getPeerID(), spec.getJobId(), spec.getRequiredWorkers(),
									spec.getMaxFails(), spec.getMaxReplicas()));
					
					data.add(frData);
					job.removeRequest(request.getSpec().getRequestId());
				}
			}
		}
		data.addAll(getCollector().getData());
		
		return data;
	}
	
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#jobEndedInterestedIsDown(java.lang.String)
	 */
	@WebMethod
	public List<SchedulerData> jobEndedInterestedIsDown(String interestedID) {
		getJobInfo().removeJobEndInterested(interestedID);
		return null;
	}
	
	/* (non-Javadoc)
	 * @see org.ourgrid.broker.scheduler.SchedulerIF#getCompleteStatus()
	 */
	@WebMethod
	public JobWorkerStatus getCompleteStatus() {
		
		Map<Integer, Job> jobsMap = getJobInfo().getJobs();
		Map<Integer, Set<WorkerEntry>> workersByJob = new HashMap<Integer, Set<WorkerEntry>>(); 
		
		JobStatusInfo jobInfo = null;
		List<TaskStatusInfo> tasksList = null;
		Map<Integer, JobStatusInfo> jobs = new LinkedHashMap<Integer, JobStatusInfo>();
		
		//Jobs
		for (Job job : jobsMap.values()) {
			
			Set<WorkerEntry> workers = new HashSet<WorkerEntry>();
			tasksList = new ArrayList<TaskStatusInfo>();

			for (Task task : job.getTasks()) {
				tasksList.add(fillTask(task));
				for (GridProcess gridProcess : task.getGridProcesses()) {
					if (gridProcess.getState() == GridProcessState.RUNNING)
						workers.add(gridProcess.getWorkerEntry());
				}
			}
			
			jobInfo = new JobStatusInfo(job.getJobId(), job.getSpec(), UtilConverter.getJobState(job.getState()),
					tasksList);
			
			jobs.put(jobInfo.getJobId(), jobInfo);
			
			workersByJob.put(job.getJobId(), workers);
		}
		
		Map<Integer, WorkerStatusInfo[]> workers = new LinkedHashMap<Integer, WorkerStatusInfo[]>();
		
		WorkerStatusInfo[] workerList = null;
		for (Entry<Integer, Set<WorkerEntry>> entry : workersByJob.entrySet()) {
			
			workerList = workers.get(entry.getKey());
			if (workerList == null) {
				workerList = new WorkerStatusInfo[entry.getValue().size()];
				workers.put(entry.getKey(), workerList);
			}
			
			int i = 0;
			for (WorkerEntry workerEntry : entry.getValue()) {
				
				GridProcessHandle handle = null;
				String state = null;
				
				if (workerEntry.getGridProcess() != null) {
					handle = workerEntry.getGridProcess().getHandle();
					state = workerEntry.getGridProcess().getState().toString();
				}
				
				workerList[i] = new WorkerStatusInfo(workerEntry.getWorkerSpec(), 
						handle, workerEntry.getWorkerID(), state);
				i++;
			}
		}
		
		JobWorkerStatus status = new JobWorkerStatus(jobs, workers);
		
		return status;
	}
	
	private TaskStatusInfo fillTask(Task task) {
		
		List<GridProcessStatusInfo> processesList = new ArrayList<GridProcessStatusInfo>();
		
		for (GridProcess process : task.getGridProcesses()) {
			processesList.add(fillProcess(process));
		}
		
		TaskStatusInfo taskInfo = new TaskStatusInfo(task.getTaskid(), task.getJobId(), 
				UtilConverter.getJobState(task.getState()),
				task.getActualFails(), task.getSpec(), processesList);
		
		return taskInfo;
	}
	
	
	private GridProcessStatusInfo fillProcess(GridProcess process) {
		
		WorkerStatusInfo workerInfo = 
			new WorkerStatusInfo(process.getWorkerEntry().getWorkerSpec(), process.getHandle());
		
		GridProcessStatusInfoResult result = null;
		
		if (process.getResult() != null) {
			
			int error = -1;
			String errorCause = null;
			
			GridProcessError executionError = process.getResult().getExecutionError();
			if (executionError != null) {
				error = UtilConverter.getProcessError(executionError.getType());
				
				if (executionError.getErrorCause() != null) {
					errorCause = executionError.getErrorCause().getMessage();
				}
			}
			
			result = new GridProcessStatusInfoResult(error, errorCause,
					process.getResult().getInitData().getElapsedTimeInMillis(),
					process.getResult().getRemoteData().getElapsedTimeInMillis(),
					process.getResult().getFinalData().getElapsedTimeInMillis(),
					process.getResult().getExecutorResult());
		}
		
		GridProcessStatusInfo info =  new GridProcessStatusInfo(process.getId(), process.getTaskId(), 
				process.getJobId(),
				UtilConverter.getJobState(process.getState()), UtilConverter.getPhase(process.getCurrentPhase()),
				workerInfo, result, process.getHandle());
		
		return info;
	}
	
	private List<SchedulerData> addJob(WorkerSpec workerSpec, long requestID, Job job, String workerID) {
		
		List<SchedulerData> data = new ArrayList<SchedulerData>();
		data.addAll(cancelPreviousWorkerExecution(workerSpec.getServiceID().getContainerID().toString()));
		
		WorkerEntry workerEntry = job.addWorker(requestID, workerSpec, workerID);
		DeploymentID workerDeploymentID = new DeploymentID(workerID);
		
		getWorkerInfo().addWorkerEntry(workerDeploymentID.getContainerID().toString(), workerEntry);
		
		String xml = getXmlCreator(UpdateSchedulerXMLCreator.class).getXML();
		SchedulerData usData = new SchedulerData(xml);
		
		data.add(usData);
		data.addAll(getCollector().getData());
		
		return data;
	}
	
	private List<SchedulerData> cancelPreviousWorkerExecution(String containerID) {
		
		WorkerEntry previousEntry = getWorkerInfo().getWorker(containerID);
		
		if (previousEntry != null) {
			
			GridProcess gridProcess = previousEntry.getGridProcess();
			if (gridProcess != null) {
				gridProcess.getResult().setExecutionError(
						new GridProcessError(GridProcessErrorTypes.MACHINE_FAILURE));
				
				gridProcess.setGridProcessResult(GridProcessState.FAILED);
				
				GridProcessOperations operations = gridProcess.getOperations();
				if (operations != null) {
					operations.cancelOperations();
				}

				this.executionController.executionFailed(gridProcess);			
			}
		}
		
		return new ArrayList<SchedulerData>();
	}

	
	private List<SchedulerData> createDataList(SchedulerData... schedulerData) {
		
		List<SchedulerData> list = new ArrayList<SchedulerData>();
		
		for (SchedulerData data : schedulerData) {
			list.add(data);
		}
		
		return list;
	}
	
	private WorkerEntry getWorkerEntry(String workerContainerID) {
		WorkerEntry workerEntry = getWorkerInfo().getWorker(workerContainerID);	
		return workerEntry;
	}
	
	private SchedulerData logInvalidWorkerEntry(String workerContainerID) {
		
		return new SchedulerData(getXmlCreator(LoggerXMLCreator.class).
				getXML(WorkerClientMessages.getNotAvaliableWorker(workerContainerID), 
						LoggerXMLCreator.WARN));
	}
	
	private boolean continueExecution(WorkerEntry workerEntry) {
		
		if (workerEntry == null) {
			return false;
		}
		
		GridProcess execution = workerEntry.getGridProcess();
		
		if (execution == null) {
			return false;
		} else {
			return true;
		}
	}

	private JobInfo getJobInfo() {
		return JobInfo.getInstance();
	}

	private PeerInfo getPeerInfo() {
		return PeerInfo.getInstance();
	}

	private WorkerInfo getWorkerInfo() {
		return WorkerInfo.getInstance();
	}

	/**
	 * Executes the grid process of this job that are ready to run.
	 * @param job
	 * @param heuristic 
	 */
	private List<SchedulerData> execute(Job job) {
		
		List<SchedulerData> data = new ArrayList<SchedulerData>();
		
		for (Task task : job.getTasks()) {
			for (GridProcess gridProcess : task.getReadyToRunGridProcesses()) {
				
				ReplicaParser replicaParser = new ReplicaParser(gridProcess, new MatcherImpl());
				replicaParser.parse();

				GridProcessOperations executionOperations = replicaParser.getExecutionOperations();
				
				SchedulerData loggerData = new SchedulerData(getXmlCreator(LoggerXMLCreator.class).
						getXML(SchedulerMessages.getExecutingReplicaMessage(
								gridProcess.getHandle(), gridProcess.getWorkerEntry().getWorkerID()), 
								LoggerXMLCreator.DEBUG));
				
				data.add(loggerData);
				
				gridProcess.setOperations(executionOperations);
				gridProcess.setAsRunning();
				
				SchedulerData swData = new SchedulerData(getXmlCreator(StartWorkXMLCreator.class).
						getXML(gridProcess.getWorkerEntry().getWorkerID(), 
								gridProcess.getWorkerEntry().getRequestID(),
								gridProcess.getHandle().getJobID(), gridProcess.getHandle().getTaskID(),
								gridProcess.getHandle().getReplicaID()));
				
				data.add(swData);
			}
		}
		return data;
	}
	
	private List<SchedulerData> cleanJob(Job job) {
		
		List<SchedulerData> data = new ArrayList<SchedulerData>();
		
		if (this.executionController.isJobSatisfied(job)) {
			
			SchedulerData prData = null;
			for (Request request : job.getRequests()) {
				prData = new SchedulerData(getXmlCreator(PauseRequestXMLCreator.class).
						getXML(request.getPeerID(), request.getSpec().getRequestId()));
				
				data.add(prData);
			}

			// Dispose some workers that were received but were not needed
			
			SchedulerData loggerData = null;
			SchedulerData dwData = null;
			for (WorkerEntry workerEntry : job.getDeallocatedWorkerEntries()) {
				getWorkerInfo().removeWorker(workerEntry.getServiceID().getContainerID().toString());
				
				loggerData = new SchedulerData(getXmlCreator(LoggerXMLCreator.class).
						getXML(SchedulerMessages.getDisposingWorkerMessage(workerEntry.getServiceID()), 
								LoggerXMLCreator.WARN));
				
				dwData = new SchedulerData(getXmlCreator(DisposeWorkerXMLCreator.class).
						getXML(workerEntry.getPeerID(), workerEntry.getWorkerID()));
				workerEntry.dispose();
				
				data.add(loggerData);
				data.add(dwData);
			}
		}
		
		return data;
	}
	
	private void cancelJob(Job job) {
		
		job.setAsCanceled();
		
		
		if (job.getTasks() != null) {
			
			for ( Task task : job.getTasks() ) {
				if (task.getGridProcesses() != null) {
					
					for ( GridProcess replica : task.getGridProcesses() ) {
						replica.getOperations().cancelOperations();
					}
				}	
			}
		}
	}
	
	private SchedulerDataCollector getCollector() {
		return SchedulerDataCollector.getInstance();
	}
	
	private <U extends XMLCreatorIF> U getXmlCreator(Class<U> clazz) {
		return XMLCreatorFactory.getInstance().getXMLCreator(clazz);
	}


}
